/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.taglib.core.menu.context;

import com.blandware.atleap.webapp.taglib.core.util.ContextMenuItem;
import org.apache.struts.taglib.TagUtils;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import java.io.IOException;
import java.io.StringWriter;

/**
 * <p>Wraps item, which will be placed in some ItemContainer. This represents
 * something that can be rendered as an option (row) in context menu.</p>
 * <p>
 * Allowed attributes are:
 * <ul>
 * <li>
 * <b>enabled</b> - whether this menu item is <em>enabled</em>. This specifies
 * what child tag (<em>itemEnabled</em> or <em>itemDisabled</em>) will be
 * rendered. Also this affects what style class will be used for context menu
 * item and whether <em>itemLink</em> tag will generate a hyperlink while
 * rendering to page (see {@link ContextMenuTag}). This can be "true" or
 * "false", default is "true".
 * </li>
 * <li>
 * <b>includeInContextMenu</b> - whether to render this item to context menu.
 * If not, no context menu item corresponding to this one will be displayed in
 * context menu. Useful when you don't want to include an item to menu if some
 * condition is met. This can be "true" or "false", default is "true".
 * </li>
 * <li>
 * <b>includeInPage</b> - whether to render this item to page. Useful when you
 * don't want to include a corresponding link to a page and just want it to be
 * in context menu. This can be "true" or "false", default is "true".
 * </li>
 * </ul>
 * </p>
 * <p><a href="ItemTag.java.html"><i>View Source</i></a></p>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.7 $ $Date: 2006/08/03 10:08:25 $
 * @jsp.tag name="item"
 * body-content="scriptless"
 * @see ContextMenuTag
 * @see ItemSetTag
 * @see ItemEnabledTag
 * @see ItemDisabledTag
 */
public class ItemTag extends ItemContainerSupport implements ItemContainer {

	/**
	 * Whether or not this item is enabled
	 */
	protected Boolean enabled = Boolean.TRUE;

	/**
	 * Whether or not to add this item to the generated context menu
	 */
	protected Boolean includeInContextMenu = Boolean.TRUE;

	/**
	 * Whether or not to include this item to page
	 */
	protected Boolean includeInPage = Boolean.TRUE;

	/**
	 * Item we're currently creating
	 */
	protected ContextMenuItem item = null;

	/**
	 * Creates new instance of ItemTag
	 */
	public ItemTag() {
	}

	/**
	 * Returns whether or not this item is enabled
	 *
	 * @return whether this item is enabled
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.Boolean"
	 * description="Whether or not this item is enabled on current row"
	 */
	public Boolean getEnabled() {
		return enabled;
	}

	/**
	 * Sets whether or not this item is enabled
	 *
	 * @param enabled whether this item is enabled
	 */
	public void setEnabled(Boolean enabled) {
		this.enabled = enabled;
	}

	/**
	 * Returns whether or not to add this item to the generated context menu
	 *
	 * @return whether to add this item to the generated context menu
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.Boolean"
	 * description="Whether or not to include this item in context menu"
	 */
	public Boolean getIncludeInContextMenu() {
		return includeInContextMenu;
	}

	/**
	 * Sets whether or not to add this item to the generated context menu
	 *
	 * @param includeInContextMenu whether to add this item to the generated context menu
	 */
	public void setIncludeInContextMenu(Boolean includeInContextMenu) {
		this.includeInContextMenu = includeInContextMenu;
	}

	/**
	 * Returns whether or not to include this item to page
	 *
	 * @return whether to include this item to page
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.Boolean"
	 * description="Whether or not to render this item to page"
	 */
	public Boolean getIncludeInPage() {
		return includeInPage;
	}

	/**
	 * Sets whether or not to include this item to page
	 *
	 * @param includeInPage whether to include this item to page
	 */
	public void setIncludeInPage(Boolean includeInPage) {
		this.includeInPage = includeInPage;
	}


	/**
	 * Returns item we're currently creating. Used by child tags to set, for example, enabled and disabled parts of item
	 *
	 * @return ContextMenuItem instance
	 */
	public ContextMenuItem getItem() {
		return item;
	}

	/**
	 * Performs tag's logic
	 *
	 * @throws JspException
	 * @throws IOException
	 */
	public void doTag() throws JspException, IOException {

		// search for parent item container
		ItemContainer container = (ItemContainer) findAncestorWithClass(this, ItemContainer.class);

		if ( container == null ) {
			throw new JspTagException("This tag is only valid when nested within some ItemContainer");
		}

		// ensure that all flags are set correclty

		if ( enabled == null ) {
			enabled = Boolean.FALSE;
		}

		if ( includeInContextMenu == null ) {
			includeInContextMenu = Boolean.TRUE;
		}

		if ( includeInPage == null ) {
			includeInPage = Boolean.TRUE;
		}

		// calculate index
		Integer index = new Integer(0);

		// search for parent ItemSet tag
		ItemSetTag itemSetTag = (ItemSetTag) findAncestorWithClass(this, ItemSetTag.class);
		if ( itemSetTag != null ) {
			index = itemSetTag.getIndex();
		}

		// search for parent ItemTag if any
		ItemTag parentItemTag = (container instanceof ItemTag) ? (ItemTag) container : null;

		// obtain and invoke body content
		// create new instance of ContextMenuItem
		item = new ContextMenuItem();

		if ( !includeInContextMenu.booleanValue() || (parentItemTag != null && !parentItemTag.getEnabled().booleanValue()) ) {
			item.setHidden(Boolean.TRUE);
		}

		// Get body content to memory to decide whether we need to write it to
        // page. We can't just skip body because it may contain tags that will
        // form correct context menu structure.
		StringWriter writer = new StringWriter();
		JspFragment body = getJspBody();
		if ( body != null ) {
			body.invoke(writer);
		}
		if ( includeInPage.booleanValue() ) {
			TagUtils.getInstance().write((PageContext) getJspContext(), writer.toString());
		}

		// items have been filled with nested item tags
		if ( item != null ) {
			item.setChildItems(menus);
		}

		// item has been filled with content, child items were also added by nested tags
		// we can add this item to the enclosing item container

		container.storeItem(index, item);

	}

	/**
	 * Overriden method from ItemContainerSupport. Ignores index, because ItemTag instance is re-created on each
	 * iteration and there is no way to save matrix, which would contain items for all rows. Matrix can be saved
	 * on top-level container only (in our case it is ContextMenuTag). ItemTag can hold vector only,
	 * and that's enough in all cases.
	 *
	 * @see ItemContainer#storeItem(Integer, com.blandware.atleap.webapp.taglib.core.util.ContextMenuItem)
	 */
	public void storeItem(Integer index, ContextMenuItem item) {
		menus.add(item);
	}
}
